package com.huanyu.backend.study.datastructure.queue;

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

import java.util.*;
import java.util.concurrent.*;

/**
 * 收银台排队结账的例子，基于用户结账，使用非阻塞队列
 * LinkedList
 * 实现Deque接口
 *
 * PriorityQueue
 * 加入到 Queue 中的元素根据它们的天然排序（通过其 java.util.Comparable 实现）
 * 或者根据传递给构造函数的 java.util.Comparator 实现来定位
 *
 * ConcurrentLinkQueue
 * 基于链接节点的、线程安全的队列
 * 收集关于队列大小的信息会很慢，需要遍历队列
 * 并发访问不需要同步，因为它在队列的尾部添加元素并从头部删除它们
 * 采用CAS机制（compareAndSwapObject原子操作）
 * @author arron
 */
public class CheckoutExample {

    /**
     * 用户
     */
    @AllArgsConstructor
    @NoArgsConstructor
    private static class Person implements Comparable<Person> {
        private String name;

        private String paySomething() {
            return name + " pay something";
        }

        // 对象自身具备比较性
        @Override
        public int compareTo(Person o) {
            return name.compareTo(o.name);
        }
    }

    /**
     * 给容器传入比较器
     */
    private static class PersonComparator implements Comparator<Person> {

        @Override
        public int compare(Person o1, Person o2) {
            return o1.name.compareTo(o2.name);
        }
    }

    /**
     * 收银员
     */
    @AllArgsConstructor
    @NoArgsConstructor
    private static class Cashier {
        private String name;

        private String checkout(Person person) {
            try {
                // 休息5毫秒模拟支付消耗
                TimeUnit.MILLISECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Cashier " + name + " checkout: " + person.paySomething();
        }

        private static void cashierCheckoutPeople(String cashierName, Queue<Person> people) {
            Cashier cashier = new Cashier(cashierName);
            while (!people.isEmpty()) {
                Person person = people.poll();
                String cashierResult = cashier.checkout(person);
                System.out.println(cashierResult);
            }
        }
    }

    /**
     * 单个收银员，多个用户排队付款
     */
    public static void singleCashierAndManyPersonUseLinkedList() {
        LinkedList<Person> linkedList = new LinkedList<>();
        for (Person person : initPeople()) {
            linkedList.offer(person);
        }

        Cashier.cashierCheckoutPeople("linkedListCashier", linkedList);
        System.out.println("linkedListCashier has checkout all person!");
    }

    /**
     * 单个收银员，多个用户排队付款
     */
    public static void singleCashierAndManyPersonUsePriorityQueue() {
        PriorityQueue<Person> priorityQueue = new PriorityQueue<>();
        for (Person person : initPeople()) {
            priorityQueue.offer(person);
        }

        Cashier.cashierCheckoutPeople("priorityQueueCashier", priorityQueue);
        System.out.println("priorityQueueCashier has checkout all person!");
    }

    /**
     * 多个收银员，多个用户排队付款
     */
    public static void manyCashierAndManyPeronUseConcurrentLinkedQueue() {
        ConcurrentLinkedQueue<Person> concurrentLinkedQueue = new ConcurrentLinkedQueue<>();
        for (Person person : initPeople()) {
            concurrentLinkedQueue.offer(person);
        }

        CountDownLatch countDownLatch = new CountDownLatch(2);
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(() -> {
            Cashier.cashierCheckoutPeople(
                    "oneConcurrentLinkedQueueCashier",
                    concurrentLinkedQueue
            );
            countDownLatch.countDown();
        });

        executorService.submit(() -> {
            Cashier.cashierCheckoutPeople(
                    "twoConcurrentLinkedQueueCashier",
                    concurrentLinkedQueue
            );
            countDownLatch.countDown();
        });

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.shutdown();
        System.out.println("Cashiers has checkout all person!");
    }

    private static List<Person> initPeople() {
        List<Person> people = new ArrayList<>(10);
        Person one = new Person("one");
        people.add(one);
        Person two = new Person("two");
        people.add(two);
        Person three = new Person("three");
        people.add(three);
        Person four = new Person("four");
        people.add(four);
        Person five = new Person("five");
        people.add(five);
        Person six = new Person("six");
        people.add(six);
        Person seven = new Person("seven");
        people.add(seven);
        Person eight = new Person("eight");
        people.add(eight);
        Person nine = new Person("nine");
        people.add(nine);
        Person ten = new Person("ten");
        people.add(ten);
        return people;
    }

    public static void main(String[] args) {
        // 按添加顺序消费
        singleCashierAndManyPersonUseLinkedList();
        // 按排序后的顺序消费
        singleCashierAndManyPersonUsePriorityQueue();
        // 多线程按全局顺序消费
        manyCashierAndManyPeronUseConcurrentLinkedQueue();
    }
}
